home *** CD-ROM | disk | FTP | other *** search
- //
- // MiscClipTextFieldCell.h -- a cell for displaying long string values
- // Written and Copyright (c) 1995 by Balazs Pataki.
- // Version 1.0. All rights reserved.
- //
- // This notice may not be removed from this source code.
- //
- // This object is included in the MiscKit by permission from the author
- // and its use is governed by the MiscKit license, found in the file
- // "LICENSE.rtf" in the MiscKit distribution. Please refer to that file
- // for a list of all applicable permissions and restrictions.
- //
-
- #import <appkit/appkit.h>
- #import <misckit/MiscString.h>
- #import <objc/objc-runtime.h>
- #import "MiscClipTextFieldCell.h"
-
-
- #define CLASS_NAME "MiscClipTextFieldCell"
- #define CLASS_VERSION 1
-
-
- #define DEFAULTCLIPPER "..."
- #define DELIMITERS [delimiters stringValue]
-
-
- // To make texts displayed in cells looking good inside views, the maximum
- // width of a cell is 4.0 points (o whatever) less than the width of the view
- // the cell is displayed in. So, we need this little adjustment (am I right?
- // - maybe not but at least it works)
- #define CELL_ADJUSTMENT 4.0
- #define BEZELED_ADJUSTMENT 5.0
- #define BORDERED_ADJUSTMENT 4.0
-
-
- #define FLUSH [[[control window] reenableFlushWindow] flushWindow];
-
-
- @interface MiscClipTextFieldCell (Private)
- - _clipStringValue:(const char *)aString;
- - _copyClipperObj:obj fromZone:(NXZone *)zone;
- - _copyDelimitersObj:obj fromZone:(NXZone *)zone;
- - _copyFullStringObj:obj fromZone:(NXZone *)zone;
- @end
-
-
- /*
- ********************************
- * *
- * MiscClipTextFieldCell *
- * *
- ********************************
- */
-
- @implementation MiscClipTextFieldCell
-
- + initialize
- // Set class version
- {
- if (self == objc_lookUpClass(CLASS_NAME)) {
- [self setVersion:CLASS_VERSION];
- }
- return self;
- }
-
- - init;
- // Initializes a newly allocated MiscClipTextFieldCell with default values,
- // that is the clipper string to "...", uses no delimiters when clipping, and
- // clipping happens on the right. The cell's other attributes are set for
- // displaying text only (no scroll, selection allowed).
- {
- return [self initTextCell:""];
- }
-
- - initTextCell:(const char*)aString;
- {
- [super initTextCell:aString];
-
- clipper = [[MiscString allocFromZone:[self zone]]
- initString:DEFAULTCLIPPER];
- fullString = [[MiscString allocFromZone:[self zone]] init];
- delimiters = nil; /* As a deafult we don't have delimiters*/
- clipOnRight = YES; /* and clipping happens on the right */
- clipEnabled = YES;
-
- [self setDelegate:self];
- // MiscClipField is initialized for only displaying its string value
- [self setBezeled:YES];
- [self setEditable:NO];
- [self setScrollable:NO];
- [self setBackgroundGray:NX_LTGRAY];
-
- return self;
- }
-
- - copyFromZone:(NXZone *)zone
- {
- id obj = [super copyFromZone:zone];
-
- [obj _copyClipperObj:clipper fromZone:zone];
- [obj _copyDelimitersObj:delimiters fromZone:zone];
- [obj _copyFullStringObj:fullString fromZone:(NXZone *)zone];
-
- return obj;
- }
-
- - free
- {
- [fullString free];
- [clipper free];
- if (delimiters)
- [delimiters free];
-
- return [super free];
- }
-
-
- - setDelegate:anObject
- {
- delegate = anObject;
- return self;
- }
-
-
-
- - setClipOnRight:(BOOL) flag
- // If flag is YES clipping happens on the right, otherwise on the left of the
- // string in the cell
- {
- clipOnRight = flag;
-
- return self;
- }
-
-
- - setClipperString:(const char*) aString
- // Sets `aString' as the string that is displayed in place of the clipped part
- // of the original string
- {
- if (clipper)
- [clipper setStringValue:aString];
- else
- clipper = [MiscString newWithString:aString];
-
- return self;
- }
-
-
- - setClipDelimiters:(const char*) delimChars
- // Sets `delimChars' as delimiters by which the clipping has to happen
- {
- if (delimiters)
- [delimiters setStringValue:delimChars];
- else
- delimiters = [MiscString newWithString:delimChars];
-
- return self;
- }
-
- - setClipEnabled:(BOOL) flag
- // Sets whether the next `setStringValue:' message should clip the text or not
- {
- clipEnabled = flag;
- return self;
- }
-
-
- - setStringValue:(const char *)aString
- {
- if ([fullString cmp:aString] != 0)
- [fullString setStringValue:aString];
-
- if ( clipEnabled )
- return [self _clipStringValue:aString];
-
- return [super setStringValue:aString];
-
- }
-
- - takeStringValueFrom:sender
- {
- [self setStringValue:[sender stringValue]];
- return self;
- }
-
- - resetStringValue:sender
- {
- if ( clipEnabled )
- return [self _clipStringValue:[self fullStringValue]];
-
- return [super setStringValue:[self fullStringValue]];
- }
-
-
- - (const char*) fullStringValue { return [fullString stringValue]; }
- - (BOOL) isClipEnabled { return clipEnabled; }
- - (BOOL) doesClipOnRight { return clipOnRight; }
- - clipper { return clipper; }
- - delimiters { return delimiters; }
- - delegate { return delegate; }
-
- - (BOOL) isWrapped
- // Returns yes if the Cell wraps the text by word
- {
- return ( cFlags2.noWrap ? NO : YES );
- }
-
-
- - write:(NXTypedStream *)stream
- {
- [super write:stream];
-
- NXWriteObject(stream, fullString);
- NXWriteObject(stream, clipper);
- NXWriteObject(stream, delimiters);
- NXWriteObject(stream, delegate);
- NXWriteType(stream, @encode(BOOL), &clipOnRight);
- NXWriteType(stream, @encode(BOOL), &clipEnabled);
-
- return self;
- }
-
-
- - read:(NXTypedStream *)stream
- {
- [super read:stream];
-
- fullString = NXReadObject(stream);
- clipper = NXReadObject(stream);
- delimiters = NXReadObject(stream);
- delegate = NXReadObject(stream);
- NXReadType(stream, @encode(BOOL), &clipOnRight);
- NXReadType(stream, @encode(BOOL), &clipEnabled);
-
- return self;
- }
-
-
- - awake
- // Does nothing but with speed :-)
- {
- return self;
- }
-
-
-
- @end
-
-
- /*
- ********************************
- * *
- * MiscClipTextField(Private) *
- * *
- * -- Private Methods -- *
- * *
- ********************************
- */
-
-
- @implementation MiscClipTextFieldCell (Private)
-
- - _clipStringValue:(const char *)aString
- // A very long story...
- //
- {
- id control = nil;
- id temp = nil;
- id mainFont = [self font];
- id scrnFont = [mainFont screenFont];
- id theFont = (scrnFont ? scrnFont : mainFont); // try to use screen font
- float clipperWidth = 0;
- float clippedWidth = 0;
- float cellWidth = 0;
- BOOL doClip = NO;
- BOOL usingDelimiters = NO;
- NXRect biggestRect;
- NXSize size;
- int i=0;
-
- if (!aString)
- return self;
-
- /* More initializing */
- if (![clipper stringValue])
- [clipper setStringValue:DEFAULTCLIPPER];
-
- clipperWidth = [theFont getWidthOf:[clipper stringValue]];
-
- temp = [[MiscString alloc] initString:aString];
-
- usingDelimiters = ( delimiters && ![delimiters emptyString] ? YES : NO );
-
- // Figure out the initial cell width (the biggest possible cell width)
- // [and do some necessary adjustment too]
- control = [self controlView];
- /* Matrix will respond ... */
- if ( [control respondsTo:@selector(getCellSize:)] ) {
- NXSize size;
- [control getCellSize:&size];
- cellWidth = size.width;
- biggestRect.size.width = size.width;
- biggestRect.size.height = size.height;
- }
- else {
- [control getFrame:&biggestRect];
- cellWidth = NX_WIDTH(&biggestRect);
- }
-
- cellWidth -= CELL_ADJUSTMENT;
-
- if ([self isBezeled])
- cellWidth -= BEZELED_ADJUSTMENT;
- else if ([self isBordered])
- cellWidth -= BORDERED_ADJUSTMENT;
-
- // Configuring the cell
- [self setSelectable:NO]; /* Disable selecting and scrolling */
- [self setScrollable:NO];
-
- [[control window] disableFlushWindow];
-
- // Check whether we need to clip or not. If we do give delegate an
- // opportunity to clip the string in his custom way. If `temp's string
- // value is still long we do the real clipping. I know its not really
- // sexy doing this with a for loop but ...
- for (i=1; i<=2; i++) {
- doClip = NO;
-
- if ([theFont getWidthOf:[temp stringValue]] > cellWidth)
- doClip = YES; /* aString is too wide so do the clip */
- else { /* Try the "trick" */
- /* Set the clipped string value */
- [super setStringValue:[temp stringValue]];
- /* Calc how much space it needed to be */
- /* displayed */
- [self calcCellSize:&size inRect:&biggestRect];
- /* It didn't fit so its broken into many*/
- /* lines (ie: the cell got higher) */
- if ((size.height > NX_HEIGHT(&biggestRect)))
- doClip = YES; /* Do the clip */
- }
- if (i==1 && doClip)
- [delegate stringWillBeClipped:temp];
- }
-
-
- // Clip on the right if `aString' is too long
- if ( doClip && clipOnRight ) {
- BOOL cutMore=NO; // This becomes true if text doesn't fits into
- // the cell in the first round
- /* The loop cuts characters from the end and checks */
- /* wheter it's short enough to be displayed */
- while (YES) {
- clippedWidth=[theFont getWidthOf:[temp stringValue]];
- /* If the string is still too long */
- if ( ((clippedWidth+clipperWidth) >= cellWidth) || cutMore) {
- [temp removeFrom:[temp length]-1 length:1];
- if ( usingDelimiters ) {
- /* No delimiter found means we reached the */
- /* last delimited string element and it */
- /* can't be shortened anymore because there*/
- /* is no "next" delimter backwards */
- int from = [temp rspotOfChars:DELIMITERS];
- if ( from == -1 )
- [temp replace:[temp stringValue] with:""];
- else
- [temp removeFrom:from+1 length:[temp length]-1];
- }
- /* Last element reached and still long */
- if ([temp emptyString]) {
- [temp setStringValue:[clipper stringValue]];
- FLUSH;
- return [super setStringValue:[temp stringValueAndFree]];
- }
- cutMore = NO;
- } //End if (still too long)
- else {
- [temp concatenate:clipper];
- /* Set the clipped string value */
- [super setStringValue:[temp stringValue]];
- /* Calc how much space it needed to be */
- /* displayed */
- [self calcCellSize:&size inRect:&biggestRect];
- /* It didn't fit so its broken into two */
- /* lines (ie: the cell got higher) */
- if ( size.height > NX_HEIGHT(&biggestRect) ){
- cutMore = YES; /* Force cutting some more */
- /* Remove clipper string from end */
- [temp removeFrom:([temp length]-[clipper length])
- length:[clipper length]];
- }
- else { /* Finished, string value can be displyed */
- [temp free];
- FLUSH;
- return self;
- }
- } // End else (check if string value can really be displayed)
- } // End of string shortening loop
- }//End if (clipOnRight)
-
-
-
- // Clip on the left if `aString' is too long
- // (This code is nearly identical to the right clipping one, except that
- // it scans the string from left to right. Can you see any way to merge
- // these two in a meaningful way?)
- if ( doClip && !clipOnRight ) {
- BOOL cutMore=NO; // This becomes true if text doesn't fits into
- // the cell in the first round
- /* The loop cuts characters from the beg. of temp */
- /* and checks wheter it's short enough to be */
- /* displayed */
- while ( YES ) {
- // This sometimes lies, maybe because of the screenfont
- // - printerfont differency
- clippedWidth=[theFont getWidthOf:[temp stringValue]];
- /* If string is still too long or we are */
- /* forced to cut more ... */
- if ( ((clippedWidth+clipperWidth) >= cellWidth) || cutMore) {
- [temp removeFrom:0 length:1];
- if ( usingDelimiters ) {
- int to=[temp spotOfChars:DELIMITERS];
- /* No delimiters found means we reached the */
- /* last delimited string element and it */
- /* can't be shortened anymore because there*/
- /* is no "next" delimter forward */
- if (to == -1)
- [temp replace:[temp stringValue] with:""];
- else
- [temp removeFrom:0 to:to-1];
- }
- /* Last element reached and still long */
- if ([temp emptyString]) {
- [temp setStringValue:[clipper stringValue]];
- FLUSH;
- return [super setStringValue:[temp stringValueAndFree]];
- }
- cutMore = NO; /* Turn forcing cutting more off */
- } //Enf if (still too long)
- else {
- /* Insert clipper */
- [temp insertString:clipper];
- /* Set the clipped string value */
- [super setStringValue:[temp stringValue]];
- /* Calc how much space it needed to be */
- /* displayed */
- [self calcCellSize:&size inRect:&biggestRect];
-
- if ( size.height > NX_HEIGHT(&biggestRect) ){
- /* It didn't fit so its broken into many*/
- /* lines (ie: the cell got higher) */
- cutMore = YES; /* Force cutting some more */
- /* Remove clipper string from front */
- [temp removeFrom:0 length:[clipper length]];
- }
- else { /* Finished, string value can be displyed*/
- [temp free];
- FLUSH;
- return self;
- }
- } // End else (check if string value can really be displayed)
- } // End of string shortening loop
- }//End if (clip on left)
-
- FLUSH;
- return [super setStringValue:[temp stringValueAndFree]];
-
- }
-
- - _copyClipperObj:obj fromZone:(NXZone *)zone
- {
- id newClipper = [obj copyFromZone:zone];
-
- clipper = newClipper;
-
- return self;
- }
-
- - _copyDelimitersObj:obj fromZone:(NXZone *)zone
- {
- id newDelim = [obj copyFromZone:zone];
-
- delimiters = newDelim;
-
- return self;
- }
-
- - _copyFullStringObj:obj fromZone:(NXZone *)zone
- {
- id newString = [obj copyFromZone:zone];
-
- fullString = newString;
-
- return self;
- }
-
- @end
-
-
-
- @implementation Object (MiscClipDelegate)
- - stringWillBeClipped:theString { return self; }
- @end
-
-
- @implementation MiscClipTextFieldCell(IBStuff)
- - (const char *)getInspectorClassName
- // Return the class name of our inspector.
- {
- return "MiscClipTextFieldInspector";
- }
- @end
-
-
-